/*
-----------------------------------------------------------------------------
This file is part of NxGraphics, Multimedia Engine.
Author : Stephane Kyles. Developed in Prague, Czech Republic.
		_   __       ______                     __     _            
	   / | / /_  __ / ____/_____ ____ _ ____   / /_   (_)_____ _____
	  /  |/ /| |/_// / __ / ___// __ `// __ \ / __ \ / // ___// ___/
	 / /|  /_>  < / /_/ // /   / /_/ // /_/ // / / // // /__ (__  ) 
	/_/ |_//_/|_| \____//_/    \__,_// .___//_/ /_//_/ \___//____/  
									/_/                             
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-----------------------------------------------------------------------------
*/
#ifndef __NXGRAPHICS3D_H__
#define __NXGRAPHICS3D_H__

#include "NxGraphics_Prerequisites.h"

namespace NxGraphics_Namespace {

class NxGraphics_Export Nx3DSceneListener
{
public :
	Nx3DSceneListener(){}
	virtual ~Nx3DSceneListener(){}
	virtual void OnCollide( NxScene3DObjectEntity * Ent1, NxScene3DObjectEntity * Ent2, NxEntityEvents Event ){}
};
 
class NxGraphics_Export NxSceneTools
{
public:
	NxSceneTools( Nx3D_Scene * Scene, Ogre::Camera * Camera );
	~NxSceneTools();
	void CreateGizmo();
	void UpdateGizmo();
	void DeleteGizmo();
	void SetGizmoMode( NXTOOLS_MODE Mode );
	const NXTOOLS_MODE GetGizmoMode() const;
	void SetActiveAxis( const std::string & AxisMeshName );
	const NXTOOL_AXIS GetActiveAxis() const ;
	bool PickGizmos( Ogre::Ray &ray, int &Axis);
	Ogre::Vector3 GetGizmoIntersect( Ogre::Ray &pickRay, Ogre::Plane &planetouse, int TranslationAxis, Ogre::Vector3 &vLastPosition);
	Ogre::Plane FindGizmoTranslationPlane( Ogre::Ray &pickRay, int TranslationAxis );
	void OnKeyPressed( const OIS::KeyEvent &e );
	void OnKeyReleased( const OIS::KeyEvent &e );
	void OnKeyMouseMoved( const OIS::MouseEvent &e );
	void OnKeyMousePressed( const OIS::MouseEvent &e, OIS::MouseButtonID id );
	void OnKeyMouseReleased( const OIS::MouseEvent &e, OIS::MouseButtonID id );
private :
	NXTOOLS_MODE mCurrentGizmoMode;
	NXTOOL_AXIS mCurrentAxis;
	Ogre::Entity * mEntitySelectionOld;
	Ogre::Entity * mEntitySelection;
	Ogre::Real mGizmoScale;                             
	Ogre::SceneNode *mGizmoNode;                                  
	Ogre::SceneNode *mGizmoX;                                     
	Ogre::SceneNode *mGizmoY;                                     
	Ogre::SceneNode *mGizmoZ;                               
	Ogre::Entity *mGizmoEntities[6];                                  
	NXTOOLS_MODE mOldGizmoMode;                              
	NXTOOLS_MODE mOldGizmoAxis;                              
	Nx3D_Scene * mScene;
	Ogre::Camera * mCamera;
};


class NxGraphics_Export NxScenePhysics   
{
public:
	//! NxScenePhysics Class Constructor.
	NxScenePhysics( Nx3D_Scene * Scene );
	//! NxScenePhysics Class Destructor.
	~NxScenePhysics();
	//! Set Gravity Force.
	void SetGravity( const Ogre::Vector3 & GravityVector );
	//! get Number of Fluids in Scene.
	unsigned int GetNumFluids();
	//! Create Vortex.
	void CreateVortexForceField(const NxVec3& pos, NxActor* actor );
	//! Create D6 Joint.
	NxD6Joint * CreateD6Joint( NxActor* a0, NxActor* a1, const Ogre::Vector3& globalAnchor, const Ogre::Vector3& globalAxis );

private :
 Nx3D_Scene * mScene;
};

//!  A class to manage a 3D scene
/*!
	this is the base class for all 3d scenes and shouldn't be used directly.
*/
class NxGraphics_Export Nx3D_Scene : public CountedObj<Nx3D_Scene>, public Ogre::FrameListener, public NxInputManagerListener
{
protected:
	typedef map<std::string, NxScene3DObject* >::type NxNodeList;
	typedef NxMapIterator<NxNodeList> NxNodeMapIterator;
	//! Nx3D_Scene class Constructor
	Nx3D_Scene();
public:
	//!  Nx3D_Scene class Destructor
	virtual ~Nx3D_Scene();
	//! Creates the 3D scene.
	virtual void CreateScene() = 0;
	//! Deletes the 3D scene.
	virtual void DeleteScene() = 0;
	//! Get the 3D scene type.
	const NxGraphics_3D_Scene_Types GetSceneType();
	//! Create a Scene Node
	NxScene3DObject * CreateNxNode( const std::string & Name );
	//! Get Nodes Iterator.
	NxNodeMapIterator GetNxNodeIterator();
	//! get NxNode By Name;
	NxScene3DObject * GetNxNode( const std::string & NodeName ) const;
	//! Get Root Scene Node.
	Ogre::SceneNode * GetRootSceneNode();
	//! remove all SceneNodes and Attached objects.
	void DeleteNxNodes();
	//! Set resource group name.
	void SetResourceGroupName( Ogre::String ResourceName );
	//! Get resource group name.
	Ogre::String GetResourceGroupName();
	//! Add a resource location
	void AddResourceLocation( const std::string & ResourceLocation, const std::string & ResourceGroup = "General", bool Recursive = false );
	//! Get the current Scene Manager.
	Ogre::SceneManager * GetNxSceneManager();
	//! Get the current Scene Viewport.
	Ogre::Viewport * GetNxViewport();
	//! Get the current Scene RT.
	Ogre::RenderTexture * GetNxRenderTexture();

	//! Set Ambient Light.
	void SetAmbientLight( const Ogre::ColourValue Colour );
	//! Get Ambient Light.
	const Ogre::ColourValue GetAmbientLight();
	//! Set Fog intensity.
	void SetFog( Ogre::FogMode Mode, Ogre::ColourValue Color, float Intensity );
	//! Sets scene background color
    /*!
	  \param BackgroundColor the background color to set
      \sa GetNxSceneBackgroundColor
    */
	void SetViewportColour( const Ogre::ColourValue BackgroundColor );
	//! Gets the scene background color.
	const Ogre::ColourValue GetViewportColour() const;

	//! Sets scene background color.
	void SetViewportColours( const Ogre::ColourValue &topLeft, const Ogre::ColourValue &bottomLeft, const Ogre::ColourValue &topRight, const Ogre::ColourValue &bottomRight);
	//! Set Viewport Background to clear Off / On
	void SetAccumulationBuffer( bool Accumulation );
	//! Creates a skybok for the current scene manager
    /*!
	  \param SkyBoxMaterialName the skybox material name to set
	  \param TextureName the texture name to set
    */
	void SetSkyBox( bool Enable, Ogre::String SkyBoxMaterialName );
	//! Adds mesh animation to be updated
    /*!
	  \param Animation the animation to add.
    */
	void AddMeshAnimation( Ogre::AnimationState * Animation );
	//! updates scene mesh animations.
    /*!
	  \param Time the FrameStarted time.
    */
	void UpdateMeshAnimations( float Time );
	//! updates scene Node animations.
    /*!
	  \param Time the FrameStarted time.
    */
	void UpdateNodeAnimations( float Time );
	//! true if scene has 2D physics.
	bool HasPhysics2D();
	//! Creates a 2D physics engine.
	void CreatePhysicsEngine2D( float LowerBoundX, float LowerBoundY, float HigherBoundX, float HigherBoundY, float Gravity );
	//! delete current 2d physics engine if available
	void DeletePhysicsEngine2D();
	//! Draw 2D physics debug primitives.
	void SetPhysics2DDraw( bool Draw );


	NxScenePhysics * GetPhysicScene();
	//! Update Physic System
	bool UpdatePhysics( float Time );
	//! Enable / Disable Physic Simulation
	void EnablePhysics( bool Enable );
	//! true if physics are enabled for the scene.
	bool IsPhysicsEnabled();
	//! true if scene has 3D physics.
	bool HasPhysics3D();
	//! Debug 3d Physics
	void SetDebugPhysics3D( bool Debug );

	//! Create a 3D Physx world with floor based at 0.
	void CreatePhysicsEngine3D();
	//! Delete the 3D Physx world.
	void DeletePhysicsEngine3D();
	//! reset physics engine and actors position/ orientation
	void ResetPhysicsEngine3D();
	//! Loads physx config file ( exported from Physx Plugin )
	int LoadPhysxScene( std::string pFilename, NxScene * PhysxScene, NXU::NXU_FileType type );
	//! Create Physx actor from previous file.
	void CreatePhysxActors();
	//! Reset Physx Actors Position & Orientation.
	void ResetPhysxActors();
	//! Deletes Physx Actors.
	void DeletePhysxActors();
	void SetSceneCollisionFlags();
	//! Get 3D Physx Scene pointer.
	NxScene & GetPhysicsScene3D();
	//! Get 3D Physx Main manager pointer.
	NxPhysicsSDK * GetPhysicsSdk3D();

	//! Get list of available characters
	void GetNxCharacterList( std::vector< std::string > & List );
	//! add a nxcharacter to the scene
	NxScene3DObjectCharacter * CreateNxCharacter( const std::string & CharacterName );
	//! Delete Character
	void DeleteNxCharacter( NxScene3DObjectCharacter * Character );
	//! Set Current View To character
	void SetCharacterView( NxScene3DObjectCharacter * Character, NavigationMode ViewType = ModeWalk );

  

	//! Set Mouse active / inactive for 3D scene
	void SetMouseActive( bool Active );
	//! Get mouse status for 3D scene.
	bool GetMouseActive();
	//! Set Keyboard active / inactive for 3D scene
	void SetKeyboardActive( bool Active );
	//! Get keyboard status for 3D scene.
	bool GetKeyboardActive();


	//! Shadows
	bool ShadowsCapabilities();
	bool ShadowsInitialize( Ogre::SceneManager * Manager );
	void ShadowsPrepare();
	void ShadersRefresh();
	void ShadersGenerate( Ogre::Entity * entity );
	Ogre::RTShader::ShaderGenerator * mShaderGenerator;



	//! Animation
	std::vector< Ogre::AnimationState * > GetEntityAnimationList() ;
	std::vector< Ogre::AnimationState * > MeshVertexAnimationList ;//vertex animation
	std::vector< Ogre::AnimationState * > NodeAnimationList;// node animation

	//listeners
	void AddListener( Nx3DSceneListener * listener );
	void RemoveListener( Nx3DSceneListener * listener );
	std::list< Nx3DSceneListener * > ListenersCallbackList;

	NxScene3DObjectTerrain * CreateTerrain( const NxTerrainDesc & TerrainDesc );
	NxSceneTools * GetEditorTools();

protected :
	//! Scene Physics manager.
	NxScenePhysics * mScenePhysics;
	//! Scene Tools
	NxSceneTools * mSceneTools;
	//! Ogre3D callback frameStarted
	virtual bool frameStarted( const Ogre::FrameEvent& evt );
	//! Ogre3D callback frameEnded
	virtual bool frameEnded( const Ogre::FrameEvent &evt );
	//! Ogre3D callback frameEnded
	virtual bool frameRenderingQueued(const Ogre::FrameEvent& evt);
	//! NxInput Virtuals
	void OnKeyMouseMoved( const OIS::MouseEvent &e );
	void OnKeyMousePressed( const OIS::MouseEvent &e, OIS::MouseButtonID id );
	void OnKeyMouseReleased( const OIS::MouseEvent &e, OIS::MouseButtonID id );
	void OnKeyPressed( const OIS::KeyEvent &e );
	void OnKeyReleased( const OIS::KeyEvent &e );
	void OnJoystickButtonPressed( const OIS::JoyStickEvent &arg, int button );
	void OnJoystickButtonReleased( const OIS::JoyStickEvent &arg, int button );
	void OnJoystickAxisMoved( const OIS::JoyStickEvent &arg, int axis );
	//! Current scene manager.
	Ogre::SceneManager * mSceneManager;
	//! Current Viewport.
	Ogre::Viewport * mViewport;
	//! Current 3D scene RT.
	Ogre::RenderTexture * mRtt;

	//! 3D scene type.
	NxGraphics_3D_Scene_Types mLayerType;
	//! scene resource group name.
	Ogre::String ResourceGroupName;
	//! Set Mouse active 
	bool MouseActive;
	//! Set Keyboard active 
	bool KeyboardActive ; 
	//! 3D Physx Scene.
	NxScene * nScene;
	//! 3D Physx Notification
	NxScene3DPhysics3DNotify  * nNotification;
	//! Enable / Disble 3D Physx
	bool nPhysxEnable;
	//! Last Physx Scene if Any
	std::string nPhysxScenePath ;
	//! Physx Draw Debug.
	NxPhysics3DDebugDraw * DebugPhysics ;

	//! Terrain List.
	std::vector< NxScene3DObjectTerrain * > mTerrains;
 
	//! 2D Physic Scene.
	b2World * WorldPhysics2D ; 
	//! 2D Physic Debug Scene.
	NxPhysics2DDebugDraw * Debug2D ;
	//! update sound Listener orientation and position.
	void UpdateSoundListener();
	//! 3d physx Scene Callback
	Nx3D_SceneCallback * SceneCallback;

private :

	//! NxNode List
	NxNodeList mNxNodes;



};

class Nx3DSceneDefault : public Nx3D_Scene
{
public :
	Nx3DSceneDefault( Ogre::RenderTexture * RTarget );
	~Nx3DSceneDefault();
	void CreateScene();
	void DeleteScene();
};

class OgreMaxSceneCallback;
//!  A class to Manage OgreMax .scene format exporter
class Nx3DSceneOgreMax: public Nx3D_Scene
{
public :
	Nx3DSceneOgreMax( Ogre::String Scene_File, Ogre::RenderTexture * RTarget );
	~Nx3DSceneOgreMax() ;
	void CreateScene();
	void DeleteScene();
	//! Gets the scene project file path
	std::string GetNxSceneFileFullPath();
private:
	void CreateActors();
	void ParseUserData();
	Ogre::String mSceneFile;
	OgreMax::OgreMaxScene * mSceneOgreMax ;
	OgreMaxSceneCallback * mSceneOgreMaxCallback;
};



class NxGraphics_Export Nx3DScenePtr : public shared_ptr<Nx3D_Scene> 
{
public:
	Nx3DScenePtr() :  shared_ptr<Nx3D_Scene>(){ }
    explicit Nx3DScenePtr( Nx3D_Scene * rep ) :  shared_ptr<Nx3D_Scene>(rep){ }
    Nx3DScenePtr( const Nx3DScenePtr& r ) :  shared_ptr<Nx3D_Scene>(r){ } 
};

} //NxGraphics_Namespace 
#endif